package com.example.edu.Service.ipml;

import com.example.edu.Dto.*;
import com.example.edu.Entity.Article;
import com.example.edu.Entity.Picture;
import com.example.edu.Mapper.ArticleMapper;
import com.example.edu.Mapper.Article_PictureMapper;
import com.example.edu.Mapper.PictureMapper;
import com.example.edu.Mapper.User_PictureMapper;
import com.example.edu.Service.PictureService;
import com.example.edu.Utils.CacheDataBaseUtil;
import com.example.edu.Utils.JwtUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.example.edu.Utils.Constants.*;

@Service
public class PictureServiceImpl implements PictureService {
    @Resource
    private CacheDataBaseUtil cacheDataBaseUtil;
    @Resource
    private PictureMapper pictureMapper;
    @Resource
    private User_PictureMapper user_pictureMapper;
    @Resource
    private ArticleMapper articleMapper;
    @Resource
    private Article_PictureMapper article_pictureMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    @Transactional
    public Result upload(String token, PictureUploadDto pictureUploadDto){
        try{
            return upload2(token,pictureUploadDto);
        }catch (Exception e){
            return Result.fail("上传失败");
        }
    }

    @Override
    public Result getList(String token, PageDto pageDto) {
        String telephone = JwtUtil.getUsername(token);
        int start_index = pageDto.getStartIndex();
        int count = pageDto.getCount();
        int total_count = user_pictureMapper.getRelationshipNum(telephone);
        List<Picture> pictures = pictureMapper.getPictures(start_index-1,Math.min(count,total_count-start_index+1),telephone);
        if(pictures == null)
            return Result.fail("这一页还没有图片哟");
        List<PictureListReturnDto> picture_messages = new ArrayList<>();
        for(int i = 0; i < pictures.size(); i++){
            Picture picture = pictures.get(i);
            PictureListReturnDto picture_message = new PictureListReturnDto(picture.getPicture_id(),i,BASE_URL+PICTURE_ADDRESS+"/"+picture.getPicture_id()+"."+picture.getType(),picture.getType(), picture.getSize());
            picture_messages.add(picture_message);
        }
        ListReturnDto r = new ListReturnDto(total_count,picture_messages);
        return Result.ok(r);
    }

    @Override
    public Result delete(String token, Picture pictureId) {
        String telephone = JwtUtil.getUsername(token);
        int picture_id = pictureId.getPicture_id();
        user_pictureMapper.deleteRelationship(picture_id,telephone);
        article_pictureMapper.deleteRelation(telephone,picture_id);
//        if(user_pictureMapper.getRelationship(picture_id,telephone) == 0)
//            deletePicture(picture_id);
        return Result.ok();
    }

    @Override
    public Result selectRelationArticle(String token, Picture pictureId) {
        String telephone = JwtUtil.getUsername(token);
        int picture_id = pictureId.getPicture_id();
        List<Article> relation_articles = articleMapper.selectRelationArticles(telephone,picture_id);
        if(relation_articles == null)
            return Result.fail("没有关联文章或草稿");
        List<Article> relation_articles_publish = new ArrayList<>();
        List<Article> relation_articles_draft = new ArrayList<>();
        for(Article relation_article : relation_articles){
            if(relation_article.getIs_publish() == 0){
                relation_articles_draft.add(relation_article);
            }
            else {
                relation_articles_publish.add(relation_article);
            }
        }
        return Result.ok(new PictureRelationArticleDto(relation_articles_publish,relation_articles_draft));
    }

    @Transactional(rollbackFor = Exception.class)
    public Result upload2(String token, PictureUploadDto pictureUploadDto) {
        String telephone = JwtUtil.getUsername(token);
        String md5 = pictureUploadDto.getKey();
        String type = pictureUploadDto.getType();
        //尝试获取锁
        if(!cacheDataBaseUtil.tryLock("picture_upload",md5,LOCK_TTL,TimeUnit.MINUTES))
            return Result.fail("用户较多，请稍后重试");
        try {
            // 若服务器中有该图片，则不需要存储图片，返回url即可
            if (cacheDataBaseUtil.isContainsById(PICTURE_KEY, md5, md52 -> pictureMapper.getPictureId(md52), PICTURE_CACHE_TTL, TimeUnit.HOURS)) {
                int picture_id = cacheDataBaseUtil.selectById(PICTURE_KEY, md5, Integer.class, md52 -> pictureMapper.getPictureId(md52), PICTURE_CACHE_TTL, TimeUnit.HOURS);
                // 若该用户未上传过该图片，则建立用户与图片的联系
                if (user_pictureMapper.getRelationship(picture_id, telephone) == 0)
                    user_pictureMapper.addRelationship(picture_id, telephone);
                return Result.ok(BASE_URL + PICTURE_ADDRESS + "/" + picture_id + "." + type);
            }
            // 否则上传图片，添加图片信息到数据库，并建立和用户的联系
            MultipartFile picture = pictureUploadDto.getPicture();
            double size = pictureUploadDto.getSize();
            Picture picture_message = new Picture();
            picture_message.setMd5(md5);
            picture_message.setType(type);
            picture_message.setSize(size);
            pictureMapper.addPicture(picture_message);
            stringRedisTemplate.opsForValue().set(PICTURE_KEY + md5, String.valueOf(picture_message.getPicture_id()), PICTURE_CACHE_TTL, TimeUnit.HOURS);
            user_pictureMapper.addRelationship(picture_message.getPicture_id(), telephone);
            try {
                String path = PICTURE_ADDRESS + "/" + picture_message.getPicture_id() + "." + type;
                File dest = new File(path);
                picture.transferTo(dest);
                return Result.ok(BASE_URL + path);
            } catch (IOException e) {
                System.out.println(e);
                throw new RuntimeException();
            }
        } finally {
            cacheDataBaseUtil.unlock("picture_upload",md5);
        }
    }

//    @Async
//    protected void deletePicture(int picture_id) {
//        Picture del_picture = pictureMapper.getType(picture_id);
//        String type = del_picture.getType();
//        String md5 = del_picture.getMd5();
//        String path = PICTURE_ADDRESS + "/" + picture_id + "." + type;
//        try {
//            cacheDataBaseUtil.deleteById(PICTURE_KEY,md5,md52 -> pictureMapper.deleteByMD5(md52));
//            File picture = new File(path);
//            if (picture.exists()) {
//                picture.delete();
//            }
//        } catch (Exception e){
//            System.out.println(e);
//        }
//    }

    @Scheduled(cron = "0 30 4 * * ?")
    @Transactional(rollbackFor = Exception.class)
    public void cleanUnusedPicture(){
        List<Picture> wild_pictures = pictureMapper.getWildPicture();
        List<String> md5s = new ArrayList<>();
        List<String> paths = new ArrayList<>();
        for(Picture wild_picture:wild_pictures) {
            md5s.add(wild_picture.getMd5());
            paths.add(PICTURE_ADDRESS + "/" + wild_picture.getPicture_id() + "." + wild_picture.getType());
        }
        try {
            cacheDataBaseUtil.deleteManyById(PICTURE_KEY, md5s, md5s2 -> pictureMapper.deleteByMD5s(md5s2));
            for(String path : paths){
                File file = new File(path);
                if(file.exists())
                    file.delete();
            }
        }catch (Exception e){
            System.out.println(e);
            throw new RuntimeException();
        }
    }
}
